package com.hb.trade.api.routine.controller.center;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hb.core.pojo.center.au.dto.*;
import com.hb.core.pojo.center.au.entity.User;
import com.hb.core.pojo.center.au.entity.UserVerify;
import com.hb.core.pojo.center.au.vo.UserLoginVo;
import com.hb.core.pojo.enums.LoginTypeEnum;
import com.hb.core.pojo.enums.RoleSignEnum;
import com.hb.core.pojo.enums.RoleTypeEnum;
import com.hb.core.pojo.enums.UserStatusEnum;
import com.hb.core.pojo.expert.entity.ExpertUser;
import com.hb.core.pojo.project.evaluation.dto.EvaluationCommitteeMembersDTO;
import com.hb.trade.api.routine.entity.TradeLoginUser;
import com.hb.trade.api.routine.service.RsaAlgorithmService;
import com.hb.trade.client.center.OrgClient;
import com.hb.trade.client.center.RoleClient;
import com.hb.trade.client.center.UserClient;
import com.hb.trade.client.expert.ExpertClient;
import com.hb.trade.client.project.EvaluationCommitteeClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.DisabledAccountException;
import org.fzzn.core.auth.shiro.JwtToken;
import org.fzzn.core.auth.shiro.ex.BlackListException;
import org.fzzn.core.auth.shiro.service.UserEntityService;
import org.fzzn.core.auth.shiro.util.CredentialUtil;
import org.fzzn.core.auth.shiro.util.ShiroUtil;
import org.fzzn.core.cache.redis.StringRedisService;
import org.fzzn.core.component.jwt.util.JwtUtils;
import org.fzzn.core.model.enums.AuditStatusEnum;
import org.fzzn.core.model.enums.LogicEnum;
import org.fzzn.core.model.enums.MsgEnum;
import org.fzzn.core.model.enums.UserTypeEnum;
import org.fzzn.core.model.msg.Message;
import org.fzzn.core.model.msg.ResponseDto;
import org.fzzn.core.model.user.login.LoginModel;
import org.fzzn.core.model.user.login.SimpleUserExample;
import org.fzzn.core.util.Enums;
import org.fzzn.core.util.MsgUtil;
import org.fzzn.core.util.ResultUtil;
import org.fzzn.core.web.anno.Version;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 系统用户登录相关
 *
 * @author zhaojk
 */
@Slf4j
@Version(1.0)
@Api(tags = {"系统用户登录相关接口"})
@RequestMapping("/{version}")
@RestController
public class LoginController {

    protected final UserClient userClient;
    protected final RoleClient roleClient;
    protected final OrgClient orgClient;
    protected final UserEntityService userEntityService;
    protected final StringRedisService redisService;
    protected final EvaluationCommitteeClient evaluationCommitteeClient;
    protected final ExpertClient expertClient;
    protected final RsaAlgorithmService rsaAlgorithmService;

    public LoginController(UserClient userClient,
                           RoleClient roleClient,
                           UserEntityService userEntityService,
                           StringRedisService redisService,
                           ExpertClient expertClient,
                           OrgClient orgClient,
                           RsaAlgorithmService rsaAlgorithmService,
                           EvaluationCommitteeClient evaluationCommitteeClient) {
        this.userClient = userClient;
        this.roleClient = roleClient;
        this.userEntityService = userEntityService;
        this.redisService = redisService;
        this.evaluationCommitteeClient = evaluationCommitteeClient;
        this.expertClient = expertClient;
        this.orgClient = orgClient;
        this.rsaAlgorithmService = rsaAlgorithmService;
    }

    /**
     * 用户名密码登录
     *
     * @param userDTO
     * @return
     */
//    @RsaDecrypt(paramKeyName = "password")
    @ApiOperation(value = "用户名密码登录", notes = "用户名密码登录", httpMethod = "POST")
    @PostMapping("/user/login")
    public ResponseDto<?> userLogin(@RequestBody UserLoginDTO userDTO) {
        // 参数验证
        String paramsValid = userDTO.paramsValid();
        if (StrUtil.isNotEmpty(paramsValid)) {
            return ResultUtil.fail(paramsValid);
        }

        // 查询指定用户名的用户
        SimpleUserExample simpleUserExample;
        // 拉黑或者被禁用会抛出异常
        try{
             simpleUserExample = userEntityService.getByUsername(userDTO.getUsername());
        } catch (DisabledAccountException | BlackListException e){
            return ResultUtil.fail(MsgEnum.USER_DISABLE);
        }
        TradeLoginUser tradeUser = (TradeLoginUser) simpleUserExample;

        if (tradeUser == null) {
            return ResultUtil.fail(MsgEnum.USERNAME_NOT_FOUND);
        }
        String antiPassword = CredentialUtil.md5(userDTO.getPassword(), UserDTO.INIT_SALT_VALUE);
        if (!antiPassword.equals(tradeUser.getPassword())) {
            return ResultUtil.fail(MsgEnum.WRONG_USERNAME_PASSWORD);
        }

        JwtToken token = new JwtToken(tradeUser);
        ShiroUtil.geneJsonWebToken(token);


        // 利用RSA解密密码
//        String decryptPassword = rsaAlgorithmService.decryptData(userDTO.getPassword());

//
//        JwtToken token = new JwtToken(userDTO.getUsername(), userDTO.getPassword());
//
//        ResponseDto resp = ShiroUtil.login(token);

        // 无任何返回表示登录成功
//        if (resp == null) {

        // 如果是超级管理员直接返回
        Long userId = tradeUser.getId();
        Message<User> userMessage = userClient.getByUserId(userId);
        User data = userMessage.getData();
        // 查询用户状态如果被禁用则无法登录
        if (data.getUserStatus()!=null && data.getUserStatus().equals(UserStatusEnum.DISABLE.getCode())){
            return  ResultUtil.fail(MsgEnum.USER_ACCOUNT_FORBIDDEN);
        }

        if (Enums.equals(UserTypeEnum.SUPER_ADMIN, data.getUserType())) {
            // 将token保存至服务器redis
            LoginModel<SimpleUserExample> model = LoginModel.get(tradeUser, token.getToken());
            JwtUtils.stayToken(model);
            return loginResponse(tradeUser, token.getToken());
        }

        // 如果是未审核用户直接返回用户信息
        if (data.getUserStatus().equals(UserStatusEnum.DETAILED.getCode()) || data.getUserStatus().equals(UserStatusEnum.REJECT.getCode())) {
            // 构建用户登录信息
            tradeUser.collectInfo(LoginTypeEnum.TRADE);
            // 将token保存至服务器redis
            LoginModel<SimpleUserExample> model = LoginModel.get(tradeUser, token.getToken());
            JwtUtils.stayToken(model);
            return loginResponse(tradeUser, token.getToken());
        }

        Integer roleSign = userDTO.getRoleSign();
        RoleSignEnum roleSignEnum = Enums.of(RoleSignEnum.class, roleSign);
        if (roleSignEnum == null) {
            return ResultUtil.fail("必须指定一个有效的业务角色");
        }

        // 判断该用户是否拥有指定的业务角色
        boolean hasRoleMsg = roleClient.hasRoleSign(userId, roleSign);

        // 成功登录 start
        if (hasRoleMsg) {
            // 构建用户登录信息
            tradeUser.collectInfo(LoginTypeEnum.TRADE);

            // 重新更新一下redis中的信息
            // 将token保存至服务器redis
            LoginModel<SimpleUserExample> model = LoginModel.get(tradeUser, token.getToken());
            JwtUtils.stayToken(model);

            return loginResponse(tradeUser, token.getToken());
        }
        // 成功登录 end

        return ResultUtil.fail("你还没有[" + roleSignEnum.getShowName() + "]的角色, 不能以该身份登录系统");
//        }
//        return resp;
    }

    @ApiOperation(value = "专家登录", notes = "专家登录", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名(支持身份证号作为参数)", dataTypeClass = String.class, required = true),
            @ApiImplicitParam(name = "password", value = "密码", dataTypeClass = String.class, required = true),
            @ApiImplicitParam(name = "roleSign", value = "登录角色", dataTypeClass = int.class, required = true)
    })
    @PostMapping("/user/expertLogin")
    public ResponseDto expertLogin(@RequestBody UserDTO userDTO) {
        // 参数验证
        String uname = userDTO.getUsername();
        if (StrUtil.isEmpty(uname)) {
            return ResultUtil.fail(MsgEnum.USERNAME_NOT_ALLOW_EMPTY);
        }
        String pword = userDTO.getPassword();
        if (StrUtil.isEmpty(pword)) {
            return ResultUtil.fail(MsgEnum.PASSWORD_NOT_ALLOW_EMPTY);
        }
        // 利用RSA解密密码
//        String decryptPassword = rsaAlgorithmService.decryptData(userDTO.getPassword());
//        userDTO.setPassword(decryptPassword);

        Message<EvaluationCommitteeMembersDTO> message = evaluationCommitteeClient.findMembersByMobileAnPassword(userDTO);

        if (message.isFail() || message.getData() == null) {
            return ResultUtil.fail("您输入的账号或密码有误");
        }

        EvaluationCommitteeMembersDTO evaluationCommitteeMembersDTO = message.getData();

        // 生成token
        SimpleUserExample tradeUser = new SimpleUserExample();

        // SimpleUserExample必须有id字段，否则下面生成token报错
        tradeUser.setId(evaluationCommitteeMembersDTO.getId());

        tradeUser.setUsername(userDTO.getUsername());
        tradeUser.setFullName(evaluationCommitteeMembersDTO.getMemberName());
        tradeUser.setPassword(userDTO.getPassword());

        JSONObject extData = JSONUtil.createObj();
        extData.putOpt("evaluationCommitteeId", evaluationCommitteeMembersDTO.getEvaluationCommitteeId());
        extData.putOpt("roleSign", userDTO.getRoleSign());
        tradeUser.setExtInfo(extData);

        JwtToken token = new JwtToken(tradeUser);
        ShiroUtil.geneJsonWebToken(token);

        // 将token保存至服务器redis
        LoginModel<SimpleUserExample> model = LoginModel.get(tradeUser, token.getToken());
        JwtUtils.stayToken(model);
        return loginResponse(tradeUser, token.getToken());
    }

    @ApiOperation(value = "登录抽取专家系统", notes = "代理机构登录抽取专家系统", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataTypeClass = String.class, required = true),
            @ApiImplicitParam(name = "password", value = "密码", dataTypeClass = String.class, required = true)})
    @PostMapping("/user/expertExtractLogin")
    public ResponseDto expertExtractLogin(@RequestBody UserDTO userDTO) {
        // 参数验证
        String uname = userDTO.getUsername();
        if (StrUtil.isEmpty(uname)) {
            return ResultUtil.fail(MsgEnum.USERNAME_NOT_ALLOW_EMPTY);
        }
        String pword = userDTO.getPassword();
        if (StrUtil.isEmpty(pword)) {
            return ResultUtil.fail(MsgEnum.PASSWORD_NOT_ALLOW_EMPTY);
        }
        ExpertUser user = userDTO.to(ExpertUser.class);
        Message<ExpertUser> message = expertClient.getExpertUser(user);
        return ResultUtil.msg(message);
    }

    @ApiOperation(value = "中招APP用户登录", notes = "中招APP用户登录")
    @PostMapping("/user/loginByZapp")
    public ResponseDto userLoginByZapp(@RequestBody ZappUserDTO zappUser) {
        Integer roleSign = zappUser.getRoleSign();
        if (roleSign == null) {
            return ResultUtil.fail("必须指定一个业务角色");
        }
        // 根据机构交易码判断角色是否存在
        User user = userClient.getUserByUnifiedTransactionCodeAndRoleSign(zappUser);


        TradeLoginUser tradeUser;
        if (user != null) {
            if (user.getUserStatus().equals(UserStatusEnum.DETAILED.getCode()) || user.getUserStatus().equals(UserStatusEnum.REJECT.getCode())) {
                return ResultUtil.fail("请使用账号密码登录未审核账号！");
            }
            // 查询用户状态如果被禁用则无法登录
            if (user.getUserStatus()!=null && user.getUserStatus().equals(UserStatusEnum.DISABLE.getCode())){
                return  ResultUtil.fail(MsgEnum.USER_ACCOUNT_FORBIDDEN);
            }
        } else {
            // 判断企业在本平台是否存在
            UserDTO userDTO = new UserDTO();
            userDTO.setRoleSign(roleSign);
            userDTO.setCreditCode(zappUser.getLegalCode());
            userDTO.setOrgName(zappUser.getLegalName());
            Message<User> userMessage = userClient.getUserByRoleSignAndOrgName(userDTO);
            if (userMessage.isSuccess()) {
                if (userMessage.getData() == null) {
                    return ResultUtil.fail(MsgEnum.USERNAME_NOT_FOUND);
                } else {
                    if (zappUser.getUserName() == null) {
                        JSONObject outDate = new JSONObject();
                        outDate.putOpt("needUserName", 1);
                        return ResultUtil.msg(MsgUtil.success(outDate));
                    }
                }
            } else {
                return ResultUtil.fail(MsgEnum.USERNAME_NOT_FOUND);
            }
        }
        UserDTO userDTO1;
        if (zappUser.getUserName() != null) {
            // 如果填写了用户名 则根据用户名查询用户
            Message<UserDTO> userDTOMessage = userClient.getByUsername(zappUser.getUserName());

            userDTO1 = userDTOMessage.getData();
            // 如果返回为空直接返回用户不存在
            if (userDTO1 == null) {
                return ResultUtil.fail(MsgEnum.USERNAME_NOT_FOUND);
            }
            // 如果用户的状态是待审核 或者 驳回状态 直接返回用账号密码登录账号
            if (userDTO1.getUserStatus().equals(UserStatusEnum.DETAILED.getCode()) || userDTO1.getUserStatus().equals(UserStatusEnum.REJECT.getCode())) {
                return ResultUtil.fail("请使用账号密码登录未审核账号！");
            }
        } else {
            userDTO1 = new UserDTO();
        }

        if (user == null && zappUser.getUserName() != null && zappUser.getPassword() != null) {
            // 根据手机号查询不到该用户，
            try{
                tradeUser = (TradeLoginUser) userEntityService.getByUsername(zappUser.getUserName());
            }catch (DisabledAccountException | BlackListException e){
                return ResultUtil.fail(MsgEnum.USER_DISABLE);
            }
            if (userDTO1.getPhone() != null && !userDTO1.getPhone().equals(zappUser.getTelephoneNum())) {
                return ResultUtil.fail("中招绑定手机号与账号绑定手机号不符！");
            }
        } else if (user != null) {
            try{
                tradeUser = (TradeLoginUser) userEntityService.getByUsername(user.getUsername());
            }catch (DisabledAccountException | BlackListException e){
                return ResultUtil.fail(MsgEnum.USER_DISABLE);
            }
        } else {
            return ResultUtil.fail(MsgEnum.WRONG_USERNAME_PASSWORD);
        }
        // 判断CA是否已经绑定，如果没绑定，则保存ca信息
        Long userId = userClient.getUserIdByVerifyCode(zappUser.getUnifiedTransactionCode());
        if (tradeUser == null) {
            return ResultUtil.fail(MsgEnum.WRONG_USERNAME_PASSWORD);
        } else {
            // 如果填写了密码才会验证密码是否跟数据库密码是否一致
            if (zappUser.getPassword() != null) {
                String antiPassword = CredentialUtil.md5(zappUser.getPassword(), tradeUser.getSaltSign());
                if (!antiPassword.equals(tradeUser.getPassword())) {
                    return ResultUtil.fail(MsgEnum.WRONG_USERNAME_PASSWORD);
                }
                zappUser.setPassword(antiPassword);
            }
        }
        // 验证企业信息与种子干后企业信息是否一样
        Message<OrgDTO> orgDTOMessage = orgClient.getOrgById(tradeUser.getOrgId());
        if (orgDTOMessage.getData() == null) {
            return ResultUtil.fail("中招企业信息与账号信息不匹配");
        } else {
            OrgDTO orgDTO = orgDTOMessage.getData();
            // 如果企业不是总公司则查询总公司信息 2022年4月28日 产品说分公司跟总公司用的一套企业信息
            if (!orgDTO.getParentId().equals(0L)){
                Message<OrgDTO> headOfficeMessage = orgClient.getHeadOffice(orgDTO.getId());
                orgDTO = headOfficeMessage.getData();
            }
            if (!orgDTO.getOrgName().equals(zappUser.getLegalName()) || !orgDTO.getCreditCode().equals(zappUser.getLegalCode())) {
                return ResultUtil.fail("中招企业信息与账号信息不匹配");
            }
            OrgDTO orgMsg = zappUser.getOrg();
            orgMsg.setId(orgDTO.getId());
            orgClient.saveOrg(orgMsg);
        }

        UserVerify userVerify = new UserVerify();
        userVerify.setUserId(tradeUser.getId());
        userVerify.setVerifyType(2);
        userVerify.setAuditStatus(1);
        userVerify.setVerifyCode(zappUser.getUnifiedTransactionCode());
        userVerify.setBackInfo(zappUser.getUser().getBackInfo());
        userClient.saveVerifyByZapp(userVerify);

        // 之前已经登录过的更新一下用户信息
        UserDTO userDTO = zappUser.getUser();
        userDTO.setId(tradeUser.getId());
        // 不更新密码 姓名 身份证号
        userDTO.setPassword(null);
        userDTO.setFullName(null);
        userDTO.setIdNum(null);
        userDTO.setUnifiedTransactionCode(zappUser.getUnifiedTransactionCode());
        Message<Boolean> message = userClient.updateUser(userDTO);
        log.info("中招扫码登录用户信息更新{}: [{}]", message.isSuccess() ? "成功" : "失败", userDTO);
        // * 获取用户信息实体 end

        // 判断该用户是否拥有指定的业务角色
        boolean hasRoleMsg = roleClient.hasRoleSign(tradeUser.getId(), roleSign);
        if (hasRoleMsg) {
            tradeUser.collectInfo(LoginTypeEnum.TRADE);
        } else {
            RoleSignEnum signEnum = Enums.of(RoleSignEnum.class, roleSign);
            return ResultUtil.fail("你还没有[" + signEnum.getShowName() + "]的角色, 不能以该身份登录系统");
        }

        // 生成token
        JwtToken token = new JwtToken(tradeUser);
        ShiroUtil.geneJsonWebToken(token);

        // 将token保存至服务器redis
        LoginModel<SimpleUserExample> model = LoginModel.get(tradeUser, token.getToken());
        JwtUtils.stayToken(model);
        return loginResponse(tradeUser, token.getToken());
    }

    /**
     * 退出登录
     *
     * @return
     */
    @ApiOperation(value = "退出登录", notes = "退出登录", httpMethod = "POST")
    @PostMapping("/user/logout")
    public ResponseDto logout() {
        boolean logout = ShiroUtil.logout();
        if (logout) {
            return ResultUtil.msg(true);
        }
        log.warn("退出登录失败");
        return ResultUtil.msg(MsgEnum.FAILURE);
    }

    /**
     * 登录信息响应对象
     *
     * @param userEntity
     * @param webToken
     * @return
     */
    private ResponseDto loginResponse(SimpleUserExample userEntity, String webToken) {
        // 返回前端不显示密码字段
//        userEntity.setPassword(null);
//        JSONObject respJSON = JSONUtil.parseObj(userEntity);
        UserLoginVo loginVo = BeanUtil.copyProperties(userEntity, UserLoginVo.class);


        JSONObject extInfo = userEntity.getExtInfo();
        loginVo.setExtInfo(extInfo);
        loginVo.setToken(webToken);
        Long id = userEntity.getId();
        Message<User> userInfo = userClient.getByUserId(id);
        if (userInfo.isSuccess()) {
            loginVo.setUserType(userInfo.getData().getUserType());
        }

//         respJSON.set("biddingRoles", biddingRolesMsg.getData());
//        Message<User> userMessage = userClient.getByUserId(userEntity.getId());

        //判断是否是管理员角色
        boolean companyManagerFlag= roleClient.hasRoleType(id, RoleTypeEnum.BRANCH_COMPANY_MANAGER.getCode());

        if (!companyManagerFlag){
            companyManagerFlag= roleClient.hasRoleType(id, RoleTypeEnum.HEAD_COMPANY_MANAGER.getCode());
        }
        if (companyManagerFlag){
            loginVo.setManagerFlag(LogicEnum.YES.getCode());
        }
        return ResultUtil.msg(true, loginVo);
    }

    @ApiOperation(value = "验证中招扫码信息是否支持签章", notes = "验证扫码信息是否支持签章", httpMethod = "POST")
    @PostMapping("/user/checkSignatureByZapp")
    private ResponseDto<Boolean> checkSignatureByZapp(@RequestBody ZappUserDTO zappUser) {
        Long userId = ShiroUtil.getUserId();
        Long orgId = ShiroUtil.getOrgId();

        if (userVerifyInfoMatchZapp(userId, zappUser)) {
            return ResultUtil.msg(MsgUtil.success(true));
        }

        // 判断当前登录用户所在公司是否是子公司，如果是子公司，去找总公司的认证信息
        Message<OrgDTO> orgDTOMessage = orgClient.getOrgById(orgId);
        OrgDTO orgDTO = orgDTOMessage.getData();
        if (orgDTO != null && orgDTO.getParentId() != null) {
            if (!orgDTO.getParentId().equals(0L)){
                orgId = orgDTO.getParentId();
            }
        }

        // 获取总公司下的管理员账号(不需要区分投标人，代理机构)
        UserDTO userDTO = new UserDTO();
        userDTO.setOrgId(orgId);
        Message<User> userMessage = userClient.findManagerByOrgId(userDTO);
        if (userMessage.getData() == null) {
            return ResultUtil.fail("未找到认证信息，无法进行扫码签章");
        }

        // 使用企业管理员的认证信息并比对
        User managerUser = userMessage.getData();

        if (userVerifyInfoMatchZapp(managerUser.getId(), zappUser)) {
            return ResultUtil.msg(MsgUtil.success(true));
        }

        return ResultUtil.fail("企业信息不一致，无法进行扫码签章");
    }

    /**
     * 用户认证信息是否匹配中招扫码信息
     * @param userId
     * @param zappUser
     * @return
     */
    private Boolean userVerifyInfoMatchZapp(Long userId, ZappUserDTO zappUser) {
        // 判断当前用户是否认证，如果已认证就拿认证信息来比对
        UserVerifyDTO userVerifyParam = new UserVerifyDTO();
        userVerifyParam.setUserId(userId);
        // 中招扫码
        userVerifyParam.setVerifyType(2);
        // 认证通过
        userVerifyParam.setAuditStatus(AuditStatusEnum.PASSED.getCode());

        Message<List<UserVerifyDTO>> verifyInfoMessage = userClient.findVerifyInfo(userVerifyParam);
        List<UserVerifyDTO> userVerifyDTOList = verifyInfoMessage.getData();

        // 是否能获取到中招认证信息
        if (CollUtil.isNotEmpty(userVerifyDTOList) && StrUtil.isNotEmpty(userVerifyDTOList.get(0).getBackInfo())) {
            JSONObject obj = JSONUtil.parseObj(userVerifyDTOList.get(0).getBackInfo());
            if (obj.get("legalName").equals(zappUser.getLegalName())
                    && obj.get("legalCode").equals(zappUser.getLegalCode())) {
                return true;
            }
        }

        return false;
    }

}
